# encoding: utf-8
import pexpect, re, requests, traceback, os
from config import BaseConfig
from app.extensions import log
from .models import ProjectSvn, ProjectOutBeetle
from app.modules.common.utils import recursive_json_loads, DbCommit
from .replaceVersion import replaceMavenJarVersion, replaceMavenWarVersion
from .phpUtil import replacePhpVersion
from app.modules.notify.notifier import send_message
from app.modules.notify.models import NotifyModel
from app.modules.project import TRUNK_LIST
from app.modules.common.jsonFileUtil import getJsonFile
from .common import getProByParam


def checkSvnIsExist(trunkName, svnPath):
    """
    校验工程svn地址是否存在
    trunkName : 主干工程名
    svnPath : 分支名  例:trunk  sms-a1.212.
    """
    if str(svnPath) != str(getProjectSvnDirName(trunkName = trunkName, withTrunk = True)):
        _svn = '%s/%s' % (getProjectSvnUrl(trunkName = trunkName, withBranch = True), svnPath)
    else:
        _svn = getProjectSvnUrl(trunkName = trunkName, withTrunk = True)

    _sh = '/bin/bash -c "svn list %s"' % _svn
    output, status = pexpect.run(_sh, withexitstatus = 1, timeout = 60)
    if status == 0:
        return True
    else:
        _msg = '校验工程svn地址是否存在异常, cause: %s.' % output
        log.info('============== %s ==============' % _msg)
        return False


def checkSvnVersionIsExist(trunkName, branchNum):
    """
    校验工程svn分支版本号是否已存在
    :param trunkName:
    :param branchNum:
    :return:
    """
    _url = getProjectSvnUrl(trunkName = trunkName, withBranch = True)
    _sh = '/bin/bash -c "svn list %s | grep %s"' % (_url, branchNum)
    log.info('============== 校验工程svn分支版本号是否已存在,%s ==============' % _sh)
    output, status = pexpect.run(_sh, withexitstatus = 1, timeout = 60)
    if status == 0:
        _list = str(output).split('\r\n')
        if len(_list) != 0:
            _msg = '工程%s版本号%s已存在, cause: %s.' % (trunkName, branchNum, output)
            log.info('============== %s ==============' % _msg)
            return True
        return False
    else:
        _msg = '校验工程svn分支版本号是否已存在异常, cause: %s.' % output
        log.info('============== %s ==============' % _msg)
        return False


def getSvnLastBranchNnm(trunkName):
    _branch_num = '1.1.1'
    _url_list = []
    _url_list.append({'name': getProjectSvnDirName(trunkName, withTrunk = True),
                      'url': getProjectSvnUrl(trunkName = trunkName, withTrunk = True)})

    _info = ProjectOutBeetle.query.filter_by(trunk_name = trunkName).first()

    if _info is not None:
        return {'branch_num': _branch_num, 'list': _url_list}

    _flag = False
    _branchurl = getProjectSvnUrl(trunkName = trunkName, withBranch = True)
    _sh = '/bin/bash -c "source /etc/profile && bash %s/shell/list_svn_branches.sh %s " ' % (
        BaseConfig.PROJECT_ROOT, _branchurl)
    output, status = pexpect.run(_sh, withexitstatus = 1, timeout = 60)
    try:
        if status == 0:
            _list = str(output).split('\r\n')
            for _name in _list:
                if _name != '':
                    _url_list.append(
                        {'name': str(_name).replace('/', ''),
                         'url': '%s/%s' % (_branchurl, str(_name).replace('/', ''))})
                    if re.search("(\\S+)(_|-|\\.)(\\d+\\.)(\\d+\\.)(\\d+)(_|-|\\.)(\\S|\\s)+", _name):
                        # _flag = True
                        # _branch_num = sortBranchNum(_branch_num,
                        #                             re.sub("(\\S+)(_|-|\\.)(\\d+\\.)(\\d+\\.)(\\d+)(_|-|\\.)(\\S|\\s)+",
                        #                                    "\\3\\4\\5", _name))
                        if not _flag:
                            _branch_num = re.sub("(\\S+)(_|-|\\.)(\\d+\\.)(\\d+\\.)(\\d+)(_|-|\\.)(\\S|\\s)+",
                                                 "\\3\\4\\5",
                                                 _name)
                            _flag = True
        else:
            _msg = '校验工程 %s svn分支列表异常, cause: %s.' % (trunkName, output)
            log.info('============== %s ==============' % _msg)
        if _flag:
            # result = re.sub("(\\d+)(\\.)(\\d+)(\\.)(\\d+)", "\\3", _branch_num)
            log.info('============== %s当前最大版本号为 %s ==============' % (trunkName, _branch_num))
            # _branch_num = str(_branch_num).replace('.' + str(result) + '.', '.' + str(int(result) + 1) + '.')
            _branch_num = re.sub("(\\d+)(\\.)(\\d+)(\\.)(\\d+)", _add1, _branch_num)

    except BaseException:
        log.error(traceback.format_exc())

    # 判断获取的版本号是否已存在
    while checkSvnVersionIsExist(trunkName, _branch_num):
        log.info('============== %s版本号%s已存在 ==============' % (trunkName, _branch_num))
        _branch_num = re.sub("(\\d+)(\\.)(\\d+)(\\.)(\\d+)", _add1, _branch_num)

    log.info('============== %s新版本号为 %s ==============' % (trunkName, _branch_num))
    return {'branch_num': _branch_num, 'list': _url_list}


def _add1(matched):
    """
    新版本号为旧版本号第三位加1
    :param matched:
    :return:
    """
    _str = matched.group(5)
    return '%s%s%s%s%s' % (
        matched.group(1), matched.group(2), matched.group(3), matched.group(4), str(int(_str) + 1))


def sortBranchNum(num1, num2):
    _n1 = int(str(num1).replace('.', ''))
    _n2 = int(str(num2).replace('.', ''))
    if _n1 >= _n2:
        return num1
    else:
        return num2


def createProject(_trunk_name, _project_name, _vcsPath, _fromProject, _token_id, _plan_id, _plan_name, _plan_code,
                  _jarlist = None):
    """
    添加完工程记录后创建工程  包括创建svn目录，在beetle内创建工程，修改工程的pom文件
    :param _trunk_name:
    :param _project_name:
    :param _vcsPath:
    :param _fromProject:
    :param _token_id:
    :param _plan_name:
    :param _plan_id:
    :param _plan_code:
    :param _jarlist:
    :return:
    """
    log.info("============== 创建工程 %s 开始 ==============" % _project_name)
    sendProjectCreateMsg(_plan_id, _trunk_name, 'start_create', '开始创建')
    _project = getProByParam(project_name = _project_name)
    if _project is not None:
        try:
            log.info("============== 创建工程 %s ,工程记录已存在,拷贝svn代码 ==============" % _project_name)
            sendProjectCreateMsg(_plan_id, _trunk_name, 'start_create', '开始创建svn分支')
            # svn上创建工程 svn copy frompath topath -m 'test'
            if str(_fromProject) != str(getProjectSvnDirName(trunkName = _trunk_name, withTrunk = True)):
                _fromsvn = '%s/%s' % (
                    getProjectSvnUrl(trunkName = _trunk_name, withBranch = True), _fromProject)
            else:
                _fromsvn = getProjectSvnUrl(trunkName = _trunk_name, withTrunk = True)

            _project.from_svn_version = getSvnVersion(_fromsvn)

            _sh = '/bin/bash -c "svn copy %s %s -m \'新建项目%s工程\' "' % (_fromsvn, _vcsPath, _plan_name)
            output, status = pexpect.run(_sh, withexitstatus = 1, timeout = 60)
            if status == 0:
                log.info("============== 创建工程 %s ,拷贝svn代码完成 ==============" % _project_name)
                pass
            else:
                _msg = "创建工程 %s svn目录异常. %s" % (_project_name, output)
                log.error("============== %s ==============" % _msg)
                sendProjectCreateMsg(_plan_id, _trunk_name, 'create_error', '创建svn分支失败%s' % output)
                _project.project_status = 'PRO_CREATE_ERROR'
                _project.error_desc = _msg
                DbCommit(cleanCache = True)
                return

            # 调用beetle-api创建工程
            log.info("============== 创建工程 %s ,开始调用beetle创建工程 ==============" % _project_name)
            sendProjectCreateMsg(_plan_id, _trunk_name, 'start_create', '开始创建beetle工程')
            _list = []
            if _jarlist is not None and not projectIsMavenJar(_trunk_name):
                for _jar in _jarlist:
                    _list.append({'isDeploy': '0', 'jarName': str(_jar.project_name)})

            if TRUNK_LIST[_trunk_name] == 'ant_android':
                _vcsPath = '%s/%s' % (_vcsPath, _trunk_name)

            _url = "%s/project/addProjectForPlan?tokenId=%s&trunkName=%s&projectName=%s&vcsPath=%s&dependency=%s" % (
                BaseConfig.BEETLE_API_URL, _token_id, _trunk_name, _project_name, _vcsPath, str(_list))
            _rtn = recursive_json_loads(requests.post(url = _url).content)
            log.info("============== 创建工程 %s ,调用beetle创建工程完成 ==============" % _project_name)
            if _rtn.code != 0:
                _msg = "创建工程 %s 失败,error:%s" % (_project_name, _rtn.desc)
                log.error("============== %s ==============" % _msg)
                sendProjectCreateMsg(_plan_id, _trunk_name, 'create_error', _msg)
                _project.project_status = 'PRO_CREATE_ERROR'
                _project.error_desc = _msg
                DbCommit(cleanCache = True)
                return

            _project.local_path = _rtn.data.localPath
            _project.package_name = _rtn.data.warName

            # 修改工程版本(java 为pom.xml  php 为 version.php)文件
            log.info("============== 创建工程 %s ,修改工程版本文件 ==============" % _project_name)
            _path = '%s/projectCode' % _project.local_path
            try:
                if not os.path.exists(_path):
                    os.makedirs(_path)
                sendProjectCreateMsg(_plan_id, _trunk_name, 'start_create', '开始检出工程代码')
                _sh = '/bin/bash -c "cd %s && svn checkout %s projectCode " ' % (_project.local_path, _vcsPath)
                output, status = pexpect.run(_sh, withexitstatus = 1, timeout = 600)
                if status == 0:
                    log.info("============== 创建工程 %s ,修改工程版本文件,检出工程代码完成 ==============" % _project_name)
                    pass
                else:
                    _msg = "修改版本文件,检出工程 %s svn代码异常. %s" % (_project_name, output)
                    log.error("============== %s ==============" % _msg)
                    sendProjectCreateMsg(_plan_id, _trunk_name, 'create_error', _msg)
                    _project.project_status = 'PRO_CREATE_ERROR'
                    _project.error_desc = '修改版本文件,检出工程svn代码异常'
                    DbCommit(cleanCache = True)
                    return

                _vfile = 'pom.xml'
                if TRUNK_LIST[_trunk_name] == 'php':
                    _vfile = 'version.php'

                _pomfile = '%s/%s' % (_path, _vfile)

                if not os.path.exists(_pomfile):
                    _msg = "工程 %s 版本文件 %s 不存在." % (_project_name, _pomfile)
                    log.info("============== %s ==============" % _msg)
                else:
                    log.info("============== 创建工程 %s ,开始修改工程版本文件 ==============" % _project_name)
                    sendProjectCreateMsg(_plan_id, _trunk_name, 'start_create', '开始修改工程版本文件')

                    _v = None
                    if _vfile == 'version.php':
                        _v = replacePhpVersion(_pomfile, str(_project.branch_num))
                    else:
                        _jarObj = {}
                        if projectIsMavenJar(_trunk_name):
                            _v = replaceMavenJarVersion(_path, '%s.SNAPSHOT' % str(_project_name).replace(
                                '%s_' % _trunk_name, '').replace('.%s' % _plan_code, ''))
                        else:
                            for _jar in _jarlist:
                                _obj = {'groupId': 'com.winit',
                                        'version': '%s.SNAPSHOT' % str(_jar.project_name).replace(
                                            '%s_' % _jar.trunk_name, '').replace('.%s' % _plan_code, '')}
                                _jarObj[_jar.trunk_name] = _obj
                            _v = replaceMavenWarVersion(_trunk_name, _path, '%s.SNAPSHOT' % str(_project_name).replace(
                                '%s_' % _trunk_name, '').replace('.%s' % _plan_code, ''), _jarObj)

                    createSqlDir(_project.trunk_name, _path, _project.package_type, getFromVersion(_v),
                                 _project.branch_num)

                    _sh = '/bin/bash -c "cd %s && svn commit -m \'新建项目%s工程,修改版本文件\' "' % (_path, _plan_name)
                    output, status = pexpect.run(_sh, withexitstatus = 1, timeout = 60)
                    if status == 0:
                        log.info("============== 创建工程 %s ,修改工程版本文件,提交svn完成 ==============" % _project_name)
                        pass
                    else:
                        _msg = "修改工程 %s 版本文件,提交svn异常. %s" % (_project_name, output)
                        log.error("============== %s ==============" % _msg)
                        sendProjectCreateMsg(_plan_id, _trunk_name, 'create_error', _msg)
                        _project.project_status = 'PRO_CREATE_ERROR'
                        _project.error_desc = _msg
                        DbCommit(cleanCache = True)
                        return
                    _project.from_version = _v
            except BaseException as e:
                _msg = "修改工程 %s 版本文件异常，e:%s" % (_project_name, e.message)
                log.error("============== %s ==============" % _msg)
                log.error(traceback.format_exc())
                sendProjectCreateMsg(_plan_id, _trunk_name, 'create_error', _msg)
                _project.project_status = 'PRO_CREATE_ERROR'
                _project.error_desc = _msg
                DbCommit(cleanCache = True)
                return

            _project.project_status = 'PRO_CREATED'
            log.info("============== 创建工程 %s 完毕,保存工程信息 ==============" % _project_name)
            sendProjectCreateMsg(_plan_id, _trunk_name, 'create_success', '创建工程完毕')
            DbCommit(cleanCache = True)
            return
        except BaseException:
            _msg = "创建工程 %s 异常. %s" % (_project_name, traceback.format_exc())
            log.error("============== %s ==============" % _msg)
            sendProjectCreateMsg(_plan_id, _trunk_name, 'create_error', _msg)
            _project.project_status = 'PRO_CREATE_ERROR'
            _project.error_desc = _msg
            DbCommit(cleanCache = True)
            return
    else:
        _msg = "数据库中没有工程 %s的记录" % _project_name
        log.error("============== %s ==============" % _msg)
        sendProjectCreateMsg(_plan_id, _trunk_name, 'create_error', _msg)
    return


# 获取工程svn地址
def getProjectSvnUrl(trunkName, withTrunk = False, withBranch = False):
    _info = ProjectSvn.query.filter_by(project_name = trunkName).first()
    if _info is not None:
        if withTrunk:
            return '%s/%s' % (_info.base_svn_url, _info.trunk_dir_name)
        elif withBranch:
            return '%s/%s' % (_info.base_svn_url, _info.branches_dir_name)
        else:
            return _info.base_svn_url
    else:
        if withTrunk:
            return '%s/%s/trunk' % (BaseConfig.SVN_URL, trunkName)
        elif withBranch:
            return '%s/%s/branches' % (BaseConfig.SVN_URL, trunkName)
        else:
            return '%s/%s' % (BaseConfig.SVN_URL, trunkName)


def getProjectSvnDirName(trunkName, withTrunk = False, withBranch = False):
    _info = ProjectSvn.query.filter_by(project_name = trunkName).first()
    if _info is not None:
        if withTrunk:
            return _info.trunk_dir_name
        elif withBranch:
            return _info.branches_dir_name
        else:
            return ''
    else:
        if withTrunk:
            return 'trunk'
        elif withBranch:
            return 'branches'
        else:
            return ''


# 创建工程时,发送创建信息
def sendProjectCreateMsg(_plan_id, _trunk_name, _status, _msg):
    send_message(NotifyModel('plan_%s_create' % _plan_id, {'trunk_name': _trunk_name, 'status': _status, 'msg': _msg}))


def getSvnTrunk(trunk_name):
    if trunk_name in BaseConfig.SVN_TRUNK_NAME:
        return BaseConfig.SVN_TRUNK_NAME[trunk_name]
    else:
        return trunk_name


def projectIsMavenJar(trunk_name):
    if trunk_name not in TRUNK_LIST:
        return False
    else:
        return TRUNK_LIST[trunk_name] == 'maven_jar'


# 工程创建完成后，自动创建sql文件夹
def createSqlDir(trunk_name, codePath, packageType, fromVersion, toVersion):
    path = None
    json = getJsonFile("%s/data/sql_file_path.json" % BaseConfig.PROJECT_ROOT)
    if trunk_name in json:
        path = "%s%s" % (codePath, json[trunk_name])
    else:
        if packageType == 'php':
            path = "%s/script/sql" % codePath
        elif packageType == 'maven_war' or packageType == 'spring_boot_jar':
            path = "%s/src/main/resources/sql" % codePath
    if path is None:
        log.info('packageType %s is not current' % packageType)
        return
    if not os.path.exists(path):
        log.info('path %s not exists' % path)
        return

    log.info('path is %s' % path)
    for _dir in os.listdir(path):
        if not os.path.isdir('%s/%s' % (path, _dir)):
            log.info('%s is not a dir' % _dir)
            continue
        if str(_dir).__contains__('lock') or str(_dir).__contains__('local'):
            log.info('%s is locked' % _dir)
            continue
        log.info('=========%s' % _dir)
        newName = '%s(locked)' % _dir
        if os.path.exists('%s/%s' % (path, newName)):
            newName = '%s(lock)' % _dir

        _sh = '/bin/bash -c "cd %s && svn move %s %s"' % (
            path, str(_dir).replace('(', '\(').replace(')', '\)'), str(newName).replace('(', '\(').replace(')', '\)'))
        log.info('===========%s' % _sh)
        output, status = pexpect.run(_sh, withexitstatus = 1, timeout = 60)
        if status == 0:
            log.info('mv %s %s to %s success' % (path, _dir, newName))
        else:
            log.info('mv %s %s to %s error . %s' % (path, _dir, newName, output))

    newFile = '%s_to_%s' % (fromVersion, toVersion)
    _sh = '/bin/bash -c "cd %s && svn mkdir %s"' % (path, str(newFile))
    log.info('===========%s' % _sh)
    output, status = pexpect.run(_sh, withexitstatus = 1, timeout = 60)
    if status == 0:
        log.info('mkdir %s/%s success' % (path, newFile))
    else:
        log.info('mkdir %s/%s error . %s' % (path, newFile, output))


def getFromVersion(version):
    _rtn = None
    flag = False
    for v in str(version).split('.'):
        try:
            int(v)
            if _rtn is None:
                _rtn = v
            else:
                _rtn = '%s.%s' % (_rtn, v)
            flag = True
        except BaseException:
            if flag:
                break
        pass
    if _rtn is None:
        _rtn = '0.0.0'
    return _rtn


def getSvnVersion(svnUrl):
    """
    获取指定地址的svn版本号
    """
    version = None
    _sh = '/bin/bash -c "svn info %s |grep \'Last Changed Rev\' |awk \'{print $4}\' "' % svnUrl
    log.info('app.modules.project.util.getSvnVersion sh=%s' % _sh)
    output, status = pexpect.run(_sh, withexitstatus = 1, timeout = 60)
    if status == 0:
        version = output
        log.info('app.modules.project.util.getSvnVersion url=%s,version=%s' % (svnUrl, version))
    else:
        log.info('app.modules.project.util.getSvnVersion error. url=%s,output=%s' % (svnUrl, output))
    return version
